package com.xjrsoft.module.organization.controller;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.TypeReference;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.xjrsoft.common.annotation.XjrLog;
import com.xjrsoft.common.constant.GlobalConstant;
import com.xjrsoft.common.enums.YesOrNoEnum;
import com.xjrsoft.common.handler.FormContentStyleStrategy;
import com.xjrsoft.common.page.ConventPage;
import com.xjrsoft.common.page.PageOutput;
import com.xjrsoft.common.model.result.R;
import com.xjrsoft.common.utils.ExcelUtil;
import com.xjrsoft.common.utils.RedisUtil;
import com.xjrsoft.common.utils.VoToColumnUtil;
import com.xjrsoft.module.app.service.IAppAuthorizeService;
import com.xjrsoft.module.organization.dto.*;
import com.xjrsoft.module.organization.entity.*;
import com.xjrsoft.module.organization.service.IRoleService;
import com.xjrsoft.module.organization.service.IUserDeptRelationService;
import com.xjrsoft.module.organization.service.IUserRoleRelationService;
import com.xjrsoft.module.organization.service.IUserService;
import com.xjrsoft.module.organization.vo.*;
import com.xjrsoft.module.system.service.IAuthorizeService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

/**
 * <p>
 * 角色 前端控制器
 * </p>
 *
 * @author tzx
 * @since 2022-03-02
 */
@RestController
@RequestMapping(GlobalConstant.ORGANIZATION_MODULE_PREFIX + "/role")
@Api(value = GlobalConstant.ORGANIZATION_MODULE_PREFIX + "/role", tags = "角色")
@AllArgsConstructor
public class RoleController {

    private final IRoleService roleService;

    private final IAuthorizeService authorizeService;

    private final IAppAuthorizeService appAuthorizeService;

    private final IUserRoleRelationService userRoleRelationService;

    private final IUserService userService;

    private final RedisUtil redisUtil;

    @GetMapping(value = "/list")
    @ApiOperation(value = "角色列表（不分页）")
    public R list(RoleListDto dto) {
        List<Role> list = roleService.list(Wrappers.lambdaQuery(Role.class)
                .likeRight(StrUtil.isNotBlank(dto.getKeyword()), Role::getName, dto.getKeyword())
                        .eq(Role::getEnabledMark, YesOrNoEnum.YES.getCode())
                .select(Role.class, x -> VoToColumnUtil.fieldsToColumns(RoleListVo.class).contains(x.getProperty())));
        List<RoleListVo> roleListVos = BeanUtil.copyToList(list, RoleListVo.class);

        List<UserRoleRelation> userRoleRelations = redisUtil.get(GlobalConstant.USER_ROLE_RELATION_CACHE_KEY, new TypeReference<List<UserRoleRelation>>() {
        });

        //统计人数
        for (RoleListVo roleListVo : roleListVos) {
            roleListVo.setCount(userRoleRelations.stream().filter(x -> ObjectUtil.equals(x.getRoleId(), roleListVo.getId())).map(UserRoleRelation::getUserId).distinct().count());
        }

        return R.ok(roleListVos);
    }

    @GetMapping(value = "/page")
    @ApiOperation(value = "角色列表（分页）")
    public R page(@Valid RolePageDto dto) {

        LambdaQueryWrapper<Role> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.like(StrUtil.isNotBlank(dto.getKeyword()), Role::getName, dto.getKeyword())
                .like(StrUtil.isNotBlank(dto.getKeyword()), Role::getCode, dto.getKeyword())
                .like(StrUtil.isNotBlank(dto.getName()), Role::getName, dto.getName())
                .like(StrUtil.isNotBlank(dto.getCode()), Role::getCode, dto.getCode())
                .like(ObjectUtil.isNotEmpty(dto.getEnabledMark()), Role::getEnabledMark, dto.getEnabledMark())
                .select(Role.class, x -> VoToColumnUtil.fieldsToColumns(RolePageVo.class).contains(x.getProperty()));

        IPage<Role> page = roleService.page(ConventPage.getPage(dto), queryWrapper);
        PageOutput<RolePageVo> pageOutput = ConventPage.getPageOutput(page, RolePageVo.class);
        return R.ok(pageOutput);
    }


    @GetMapping(value = "/info")
    @ApiOperation(value = "根据id查询角色信息")
    public R info(@RequestParam Long id) {
        Role role = roleService.getById(id);
        if (role == null) {
            R.error("找不到此用户！");
        }
        return R.ok(BeanUtil.toBean(role, RoleVo.class));
    }

    @GetMapping(value = "/info/multi")
    @ApiOperation(value = "批量查询角色信息")
    public R multiInfo(@RequestParam String ids) {
        List<Role> roleList = new ArrayList<>();
        if (StrUtil.isNotBlank(ids)){
            //pgsql需要强转成Long类型数据
            List<Long> longList = Arrays.stream(ids.split(StringPool.COMMA))
                    .map(Long::parseLong)
                    .collect(Collectors.toList());
            LambdaQueryWrapper<Role> queryWrapper = Wrappers.lambdaQuery(Role.class).in(Role::getId, longList);
            roleList = roleService.list(queryWrapper);
            if (roleList == null) {
                R.error("找不到此角色！");
            }
        }
        return R.ok(BeanUtil.copyToList(roleList, RoleVo.class));
    }

    @PostMapping
    @ApiOperation(value = "新增角色")
    public R add(@Valid @RequestBody AddRoleDto dto) {
        long count = roleService.count(Wrappers.<Role>query().lambda().eq(Role::getName, dto.getName()).or().eq(Role::getCode, dto.getCode()));
        if (count > 0) {
            return R.error("角色名称或编码已存在！");
        }
        Role role = BeanUtil.toBean(dto, Role.class);
        roleService.save(role);
        CompletableFuture.runAsync(() -> {
            List<Role> list = roleService.list();
            redisUtil.set(GlobalConstant.ROLE_CACHE_KEY, list);
        });

        return R.ok(true);
    }

    @PutMapping
    @ApiOperation(value = "修改角色")
    public R update(@Valid @RequestBody UpdateRoleDto dto) {
        if (dto.getId().equals(GlobalConstant.SUPER_ADMIN_ROLE_ID)) {
            return R.error("超级管理员角色不能修改！");
        }
        long count = roleService.count(Wrappers.<Role>query().lambda()
                .eq(Role::getName, dto.getName())
                .ne(Role::getId, dto.getId())
                .or()
                .eq(Role::getCode, dto.getCode())
                .ne(Role::getId, dto.getId()));
        if (count > 0) {
            return R.error("角色名称或编码已存在！");
        }
        Role role = BeanUtil.toBean(dto, Role.class);
        roleService.updateById(role);
        CompletableFuture.runAsync(() -> {
            List<Role> list = roleService.list();
            redisUtil.set(GlobalConstant.ROLE_CACHE_KEY, list);
        });
        return R.ok(true);
    }

    @GetMapping("/user")
    @ApiOperation(value = "获取角色用户列表")
    public R userList(@RequestParam Long id) {
        List<UserRoleRelation> list = userRoleRelationService.list(Wrappers.<UserRoleRelation>query().lambda().eq(UserRoleRelation::getRoleId, id));
        if (CollectionUtils.isEmpty(list)) {
            return R.ok(new ArrayList<>());
        }
        List<Long> userIds = list.stream().map(UserRoleRelation::getUserId).collect(Collectors.toList());
        List<User> users = userService.list(Wrappers.<User>query().lambda().in(User::getId, userIds).select(User.class, x -> VoToColumnUtil.fieldsToColumns(UserRoleVo.class).contains(x.getProperty())));
        List<UserRoleVo> userRoleVos = BeanUtil.copyToList(users, UserRoleVo.class);
        return R.ok(userRoleVos);

    }

    @PostMapping("/user")
    @ApiOperation(value = "添加角色下的人员")
    public R addRoleUser(@RequestBody AddRoleUserDto roleUserDto) {
        Long roleId = roleUserDto.getId();
        if (roleUserDto.getType() == YesOrNoEnum.NO.getCode()){
            List<Long> userIds = roleUserDto.getUserIds();
            userRoleRelationService.addRoleUser(roleId, userIds);
        }else {
            //把该组织下所以的人员全部添加到角色下面
            List<Long> deptIds = roleUserDto.getDepartmentIds();
            List<UserDeptRelation> userDeptRelations= redisUtil.get(GlobalConstant.USER_DEPT_RELATION_CACHE_KEY, new TypeReference<List<UserDeptRelation>>() {
            });
            List<UserDeptRelation> list = userDeptRelations.stream().filter(x-> CollectionUtil.isNotEmpty(deptIds) && deptIds.contains(x.getDeptId())).collect(Collectors.toList());
            //去重操作，防止一个用户在多个组织下
            List<Long> userIds = list.stream().map(UserDeptRelation::getUserId).collect(Collectors.toList()).stream().distinct().collect(Collectors.toList());
            userRoleRelationService.addRoleUser(roleId, userIds);
        }
        CompletableFuture.runAsync(() -> {
            List<UserRoleRelation> list = userRoleRelationService.list();
            redisUtil.set(GlobalConstant.USER_ROLE_RELATION_CACHE_KEY, list);
        });
        return R.ok(true);
    }

    @PostMapping("/auth")
    @ApiOperation(value = "设置角色菜单权限")
    public R auth(@Valid @RequestBody SetRoleAuthDto dto) {
        if (dto.getId().equals(GlobalConstant.SUPER_ADMIN_ROLE_ID)) {
            return R.error("超级管理员角色不能设置权限！");
        }
        return R.ok(authorizeService.setRoleAuth(dto));
    }

    @GetMapping("/auth")
    @ApiOperation(value = "获取角色菜单权限")
    public R authList(@RequestParam Long id) {
        return R.ok(authorizeService.getRoleAuth(id));

    }

    @PostMapping("/app-auth")
    @ApiOperation(value = "设置角色app菜单权限")
    public R appAuth(@Valid @RequestBody SetRoleAuthDto dto) {
        if (dto.getId().equals(GlobalConstant.SUPER_ADMIN_ROLE_ID)) {
            return R.error("超级管理员角色不能设置权限！");
        }
        return R.ok(appAuthorizeService.setRoleAppAuth(dto));
    }

    @GetMapping("/app-auth")
    @ApiOperation(value = "获取角色app菜单权限")
    public R appAuthList(@RequestParam Long id) {
        return R.ok(appAuthorizeService.getRoleAppAuth(id));

    }

    @PutMapping("/status")
    @ApiOperation(value = "修改角色状态")
    public R updateEnabled(@Valid @RequestBody UpdateRoleStatusDto dto) {
        if (dto.getId().equals(GlobalConstant.SUPER_ADMIN_ROLE_ID)) {
            return R.error("超级管理员角色不能修改！");
        }
        //根据id修改角色enabledMark
        return R.ok(roleService.update(Wrappers.<Role>update().lambda().set(Role::getEnabledMark, dto.getEnabledMark()).eq(Role::getId, dto.getId())));
    }


    @DeleteMapping
    @ApiOperation(value = "删除角色")
    @XjrLog(value = "删除角色")
    public R delete(@RequestBody List<Long> ids) {
        if (ids.contains(1L)) {
            return R.error("超级管理员角色不能删除！");
        }
        roleService.removeBatchByIds(ids);

        CompletableFuture.runAsync(() -> {
            List<Role> list = roleService.list();
            redisUtil.set(GlobalConstant.ROLE_CACHE_KEY, list);
        });
        return R.ok(true);
    }

    @PostMapping(value = "/export")
    @ApiOperation(value = "导出")
    @XjrLog(value = "导出表单数据")
    public ResponseEntity<byte[]> exportData() {
        List<Role> list = roleService.list();
        List<RoleExportVo> roleExportVos = BeanUtil.copyToList(list, RoleExportVo.class);
        ByteArrayOutputStream bot = new ByteArrayOutputStream();
        EasyExcel.write(bot).head(RoleExportVo.class).automaticMergeHead(false)
                .registerWriteHandler(new FormContentStyleStrategy())
                .excelType(ExcelTypeEnum.XLSX).sheet().doWrite(roleExportVos);
        ByteArrayOutputStream resultBot = ExcelUtil.renderExportRequiredHead(bot);
        return R.fileStream(resultBot.toByteArray(), "角色导出" + ExcelTypeEnum.XLSX.getValue());
    }

    @GetMapping("/download-template")
    @ApiOperation(value = "下载模板")
    @SneakyThrows
    public ResponseEntity<byte[]> downloadTemplate() {
        List<RoleExportVo> roleExportVos = new ArrayList<>();
        ByteArrayOutputStream bot = new ByteArrayOutputStream();
        EasyExcel.write(bot).head(RoleExportVo.class).automaticMergeHead(false)
                .registerWriteHandler(new FormContentStyleStrategy())
                .excelType(ExcelTypeEnum.XLSX).sheet().doWrite(roleExportVos);
        ByteArrayOutputStream resultBot = ExcelUtil.renderExportRequiredHead(bot);
        return R.fileStream(resultBot.toByteArray(), "角色导入模板" + ExcelTypeEnum.XLSX.getValue());
    }

    @PostMapping("/import")
    @ApiOperation(value = "导入")
    public R importData(@RequestParam MultipartFile file) throws IOException {
        List<RoleExportVo> roleExportVos = EasyExcel.read(file.getInputStream()).head(RoleExportVo.class).sheet().doReadSync();
        List<Role> roleList = BeanUtil.copyToList(roleExportVos, Role.class);
        //获取所有的字典项
        List<Role> list = roleService.list();
        for (Role role : roleList) {
            List<Role> count = list.stream().filter(x -> x.getName().equals(role.getName()) || x.getCode().equals(role.getCode())).collect(Collectors.toList());
            if (count.size() > 0){
                return R.error("角色名称或编码已存在！角色名称为：" + role.getName());
            }
        }
        return R.ok(roleService.saveBatch(list));
    }
}

